#ifndef _imageTmpl_h_
#define _imageTmpl_h_

// imageTmpl.h
//
// Image template class


#ifdef WIN32
#pragma warning(disable:4786) // Disable warning regarding long symbol names caused by templates
#pragma warning(disable:4244) // Don't show possible conversion error for op+= and related
#endif

#include "image/imageTypes.h"
#include "image/imageStorageTmpl.h"

namespace simple{
    //
    //Image
    //

    template<class T, class S=ImageStorage<T> >
    class Image
    {
    public:
        typedef typename S::row_iterator row_iterator;
        typedef typename S::iterator iterator;

        static Image sNull;
        // A convient null image which is defined for every type

        Image () {}
        Image (const Rect& boundary, 
               RectImageStorage::eAlignment align = RectImageStorage::eNoAlign) 
            : _storage (boundary, align) {}

        Image (S storage) : _storage (storage) {}
        // Used when we do alignment

        virtual ~Image () {}

        Image            (const Image& src);
        Image& operator= (const Image& src);
        // Our own copy constructor and assignment operator.

        template<class T1, class S1>
        Image& operator= (const Image<T1,S1>& src)
        {
            // Our copy() function does exactly what we want
            copy (src, *this);
            return *this;
        }
        // Assignment and image type conversion

        template<class T1, class S1>
        Image (const Image<T1,S1>& src)
        {
            // Our copy() function does exactly what we want
            copy (src, *this);
        }
        // Copy constructor with image type conversion

        bool lockImage   () const { return _storage.lock ();}
        bool unlockImage () const { return _storage.unlock ();}
        // Lock/unlock our image state (and our storage)

        bool lockState   () const { return _storage.lockState ();}
        bool unlockState () const { return _storage.unlockState ();}
        // Lock/unlock only our image state

        bool isNull      () const { return _storage.isNull();}
        int  ref         () const { return _storage.ref();}
        int xoffset      () const { return _storage.xoffset();}
        int yoffset      () const { return _storage.yoffset();}

        unsigned int bytesPerPixel () const { return _storage.bytesPerPixel();}
        unsigned int rowSpacing    () const { return _storage.rowSpacing();}

        RectImageStorage::eAlignment alignment () const 
        { return _storage.alignment();}

        S&       storage ()       { return _storage;}
        const S& storage () const { return _storage;}

        const Rect&   boundary () const { return _storage.boundary();}
        int           x0       () const { return _storage.x0();}
        int           y0       () const { return _storage.y0();}
        int           x1       () const { return _storage.x1();}
        int           y1       () const { return _storage.y1();}
        unsigned int  width    () const { return _storage.width();}
        unsigned int  height   () const { return _storage.height();}

        const T* base       ()      const  { return rowAddress (y0());}
        T*       base       ()             { return rowAddress (y0());}
        const T* rowAddress (int y) const  { return _storage.rowAddress (y);}
        T*       rowAddress (int y)        { return _storage.rowAddress (y);}

        const T& getPixel (int x, int y) const         { return _storage.getPixel (x, y);}
        const T& getPixel (const Point& point) const { return _storage.getPixel (point);}
        void     setPixel (int x, int y, const T& pixel)         { _storage.setPixel (x, y, pixel);}
        void     setPixel (const Point& point, const T& pixel) { _storage.setPixel (point, pixel);}
        // Gets or sets the specified pixel. Thread-safe

        void setRow (int y, const T& pixel) { return _storage.setRow (y, pixel);}
        void setCol (int x, const T& pixel) { return _storage.setCol (x, pixel);}
        // Set of row or columns to the specified pixel. Thread-safe

        // iterators
        row_iterator       row_begin ()       { return _storage.row_begin();}
        const row_iterator row_begin () const { return _storage.row_begin();}
        row_iterator       row_end   ()       { return _storage.row_end();}
        const row_iterator row_end   () const { return _storage.row_end();}

        iterator       begin ()       { return _storage.begin();}
        const iterator begin () const { return _storage.begin();}
        iterator       end   ()       { return _storage.end();}
        const iterator end   () const { return _storage.end();}

        // Image memory operations
        bool window (const Rect& window) 
        { return _storage.window (window);}

        void trim ();
        // Duplicate the image data, if it is shared, to use the
        // minimum amount of memory possible. Use this function to
        // duplicate the underlying image data. Thread-safe.

        void rebase ();
        // Calls trim() and then adjust the origin to be (0,0). This is
        // sometimes useful for multi-step algorithms because you don't have
        // to keep track of the origin after each step.

        RectImageStorage::eAlignment bestAlignment () const;
        // Return best alignment of this image. This is a measured
        // quantity so it will work with all images.

        Image<T,S> align (RectImageStorage::eAlignment align = 
                            RectImageStorage::eNoAlign);
        // Return an image that has the specified alignment. This
        // may return a new image

        Image<T,S> duplicate (RectImageStorage::eAlignment align = 
                                RectImageStorage::eNoAlign) const;
        // Duplicate the image data such that it has the specified alignment.
        // The image data is always duplicated, unlike align()

        // Image operations with constants. Thread-safe
        void set (const T& value) { ::set (*this, value);}
        void add (const T& value);
        void sub (const T& value);
        void mul (const T& value);
        void div (const T& value);
        void scale (float scaling);

        // Conditionals
        bool isIdentical (const Image& src) const;
        // Checks for same boundary and identical image storage only

        bool operator== (const Image& src) const;
        bool operator!= (const Image& src) const 
        { return !operator== (src);}

    protected:
        S       _storage;  // Image storage
    };


    //
    // ImageLocker
    //
    // Locking is an important issue in multi-threaded designs. This
    // object will lock/unlock an Image<> object. Using a 
    // temporary instance of ImageLocker means that the object
    // is unlocked should an exception ever be thrown. By default, full
    // locking is employed unless the user specifies that only the state
    // must be loacked.
    //
    // There are some special cases, so this object can be told to unlock
    // and lock itself.

    template<class T, class S=ImageStorage<T> >
    class ImageLocker
    {
    public:
        ImageLocker (const Image<T,S>& image, bool fullLocking = true) 
            : _image(image), _fullLocking (fullLocking), _locked (false)
        { lock();}
        ~ImageLocker () { unlock();}

        void lock ()
        {
            if (!_locked)
                _fullLocking ? _image.lockImage() : _image.lockState();
            _locked = true;
        }
        void unlock ()
        {
            if (_locked)
                _fullLocking ? _image.unlockImage() : _image.unlockState();
            _locked = false;
        }
    private:
        bool              _fullLocking;
        bool              _locked;
        const Image<T,S>& _image;

        // No copy or assignment is allowed
        ImageLocker (const ImageLocker&);
        ImageLocker& operator= (const ImageLocker&);
    };


    template<class T, class S>
    Image<T,S> Image<T,S>::sNull;

    template<class T, class S>
    Image<T,S>::Image (const Image& src)
    {
        ImageLocker<T,S> srcLocking (src, false); // Lock state
        _storage = src._storage;
    }

    template<class T, class S>
    Image<T,S>& Image<T,S>::operator= (const Image& src)
    {
        ImageLocker<T,S> srcLocking (src, false); // Lock state

        // Make sure we don't copy ourself!
        if (_storage != src._storage) {
            ImageLocker<T,S> locking (*this, false); // Lock our state
            _storage = src._storage;
        }

        return *this;
    }

    template<class T, class S>
    void Image<T,S>::trim ()
    {
        ImageLocker<T,S> srcLocking (*this, false); // Lock state

        if (isNull() || ref() == 1)
          return;

        // Copy our image
        S storage;
        copy (_storage, storage);
        _storage = storage;
    }


    template<class T, class S>
    void Image<T,S>::rebase ()
    {
        trim ();
        _storage.rebase ();
    }



    template<class T, class S>
    RectImageStorage::eAlignment Image<T,S>::bestAlignment () const
    {
        if (isNull())
            return RectImageStorage::eNoAlign;

        lockImage ();
        const T* p1 = rowAddress (y0());
        const T* p2 = p1;
        if (height() > 1)
            p2 = rowAddress (y0()+1);
        unlockImage ();

        uintptr_t addr1 = reinterpret_cast<uintptr_t>(p1);
        uintptr_t addr2 = reinterpret_cast<uintptr_t>(p2);

        if (addr1 % RectImageStorage::eQuadWordAlign == 0 &&
            addr2 % RectImageStorage::eQuadWordAlign == 0)
            return RectImageStorage::eQuadWordAlign;

        else if (addr1 % RectImageStorage::eDoubleWordAlign == 0 &&
                 addr2 % RectImageStorage::eDoubleWordAlign == 0)
            return RectImageStorage::eDoubleWordAlign;

        else if (addr1 % RectImageStorage::eWordAlign == 0 &&
                 addr2 % RectImageStorage::eWordAlign == 0)
            return RectImageStorage::eWordAlign;

        return RectImageStorage::eNoAlign;
    }



    template<class T, class S>
    Image<T,S> Image<T,S>::align (RectImageStorage::eAlignment align)
    {
        ImageLocker<T,S> locking (*this); // Lock image

        if (isNull() || align == RectImageStorage::eNoAlign)
            return *this;

        // Check the alignment on two rows as a check. Just because the
        // image was created with the proper alignment, does not mean that
        // a window of this image will be properly aligned.
        const T* p1 = rowAddress (y0());
        const T* p2 = p1;
        if (height() > 1)
            p2 = rowAddress (y0()+1);

        // Verify the alignment on each row
        if (reinterpret_cast<uintptr_t>(p1) % align == 0 &&
            reinterpret_cast<uintptr_t>(p2) % align == 0)
            return *this;

        // We need to duplicate our underlying data.
        Image<T,S> dup (::duplicate (_storage, align));

        return dup;
    }

    template<class T, class S>
    Image<T,S> Image<T,S>::duplicate (RectImageStorage::eAlignment align) const
    {
        ImageLocker<T,S> locking (*this); // Lock image
        Image<T,S> dup (::duplicate (_storage, align));

        return dup;
    }


    template<class T, class S>
    void set (Image<T,S>& image, const T& value)
    {
        ImageLocker<T,S> srcLocking (image); // Lock image

        typename Image<T,S>::row_iterator i;
        unsigned int w = image.width ();
        T* p;
        for (i=image.row_begin(); i != image.row_end(); i++) {
          p = i->p;
          for (unsigned int x=0; x<w; x++)
            *p++ = value;
        }
    }

    template<> void set  (Image<Pel8>& s,   const Pel8& value);
    template<> void set  (Image<Pel32s>& s, const Pel32s& value);
    template<> void set  (Image<RGB>& s,  const RGB& value);

    template<class T, class S>
    void Image<T,S>::add (const T& value)
    {
        ImageLocker<T,S> locking (*this); // Lock image

        typename Image<T,S>::row_iterator i;
        unsigned int w = width ();
        T* p;
        for (i=row_begin(); i != row_end(); i++) {
            p = i->p;
            for (unsigned int x=0; x<w; x++)
                *p++ += value;
        }
    }

    template<class T, class S>
    void Image<T,S>::sub (const T& value)
    {
        ImageLocker<T,S> locking (*this); // Lock image

        typename Image<T,S>::row_iterator i;
        unsigned int w = width ();
        T* p;
        for (i=row_begin(); i != row_end(); i++) {
            p = i->p;
            for (unsigned int x=0; x<w; x++)
                *p++ -= value;
        }
    }

    template<class T, class S>
    void Image<T,S>::mul (const T& value)
    {
        ImageLocker<T,S> locking (*this); // Lock image

        typename Image<T,S>::row_iterator i;
        unsigned int w = width ();
        T* p;
        for (i=row_begin(); i != row_end(); i++) {
            p = i->p;
            for (unsigned int x=0; x<w; x++)
                *p++ *= value;
        }
    }

    template<class T, class S>
    void Image<T,S>::div (const T& value)
    {
        ImageLocker<T,S> locking (*this); // Lock image

        typename Image<T,S>::row_iterator i;
        unsigned int w = width ();
        T* p;
        for (i=row_begin(); i != row_end(); i++) {
            p = i->p;
            for (unsigned int x=0; x<w; x++)
                *p++ /= value;
        }
    }

    template<class T, class S>
    void Image<T,S>::scale (float scaling)
    {
        ImageLocker<T,S> locking (*this); // Lock image

        typename Image<T,S>::row_iterator i;
        unsigned int w = width ();
        T* p;
        for (i=row_begin(); i != row_end(); i++) {
            p = i->p;
            for (unsigned int x=0; x<w; x++)
                *p++ = ::scale<T> (*p, scaling);
        }
    }

    template<class T, class S>
    bool Image<T,S>::isIdentical (const Image& src) const
    {
        ImageLocker<T,S> locking (*this);
        ImageLocker<T,S> srcLocking (src);

        bool state = true;
        if (boundary() != src.boundary() || base() != src.base())
            state = false;

        return state;
    }


    template<class T, class S>
    bool Image<T,S>::operator== (const Image<T,S>& src) const
    {
        ImageLocker<T,S> locking (*this);
        ImageLocker<T,S> srcLocking (src);

        if (boundary() != src.boundary())
            return false;

        typename Image<T,S>::row_iterator i1;
        typename Image<T,S>::row_iterator i2;
        unsigned int w = width ();
        const T* p1;
        const T* p2;
        for (i1=row_begin(), i2=row_begin(); i1 != row_end(); i1++, i2++) {
            p1 = i1->p;
            p2 = i2->p;
            for (unsigned int x=0; x<w; x++)
                if (*p1++ != *p2++)
                    return false;
        }

        return true;
    }



}

#endif // _imageTmpl_h_
